All files / src/app/api/dev/tickets/[id]/assign route.ts

0% Statements 0/103
100% Branches 0/0
0% Functions 0/1
0% Lines 0/103

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104                                                                                                                                                                                                               
export const dynamic = "force-dynamic";

/**
 * Dev Ticket Assignment API
 * POST /api/dev/tickets/[id]/assign - Assign or unassign a ticket
 */

import { NextRequest, NextResponse } from 'next/server';
import { Session } from "next-auth";
import {
  withAdmin,
  withErrorHandling,
  successResponse,
  ApiError,
  ApiSuccessResponse,
  ApiErrorResponse
} from "@/lib/api";
import { RouteContext } from "@/lib/api/middleware";
import type { AuthenticatedUser } from '@/lib/api/middleware/types';
import { prisma } from '@/lib/prisma';
import { AssignDevTicketSchema } from '@/lib/validation/dev-ticket-schemas';
import { recordAssignment } from '@/lib/dev-ticket';
import { logger } from '@/lib/logging';

interface RouteParams {
  params: Promise<{ id: string }>;
}

async function handlePost(
  request: NextRequest,
  context: RouteContext | undefined,
  session: Session,
  user: AuthenticatedUser
): Promise<NextResponse<ApiSuccessResponse<unknown> | ApiErrorResponse>> {
  const { id } = await (context as RouteParams).params;
  const body = await request.json();

  const validationResult = AssignDevTicketSchema.safeParse(body);
  if (!validationResult.success) {
    throw ApiError.validation("Invalid assignment data", validationResult.error.flatten().fieldErrors);
  }

  // Get current ticket
  const ticket = await prisma.devTicket.findUnique({
    where: { id },
    select: { id: true, ticketNumber: true, assigneeId: true }
  });

  if (!ticket) {
    throw ApiError.notFound('Ticket not found');
  }

  const { assigneeId } = validationResult.data;

  // If assigning to someone, verify the user exists and is an admin
  if (assigneeId !== null) {
    const assignee = await prisma.user.findUnique({
      where: { id: assigneeId },
      select: { id: true, role: true }
    });

    if (!assignee) {
      throw ApiError.notFound('Assignee not found');
    }

    if (assignee.role !== 'ADMIN') {
      throw ApiError.badRequest('Only admin users can be assigned to dev tickets');
    }
  }

  // Update the ticket
  const updatedTicket = await prisma.devTicket.update({
    where: { id },
    data: { assigneeId },
    include: {
      reporter: {
        select: { id: true, name: true, email: true, image: true }
      },
      assignee: {
        select: { id: true, name: true, email: true, image: true }
      },
      project: {
        select: { id: true, name: true, key: true, color: true }
      },
      labels: true
    }
  });

  // Record assignment change
  await recordAssignment(id, user.id, ticket.assigneeId, assigneeId);

  logger.info(`${assigneeId ? 'Assigned' : 'Unassigned'} ticket ${ticket.ticketNumber}`, {
    category: 'DEV_TICKETS',
    ticketId: id,
    oldAssigneeId: ticket.assigneeId,
    newAssigneeId: assigneeId,
    userId: user.id
  });

  return successResponse(updatedTicket);
}

export const POST = withErrorHandling(withAdmin(handlePost));